package sidconverter.gui;

import java.awt.EventQueue;
import java.awt.FileDialog;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.PrintStream;
import java.util.prefs.Preferences;

import javax.swing.filechooser.FileFilter;

import javax.swing.GroupLayout;
import javax.swing.GroupLayout.Alignment;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
import javax.swing.LayoutStyle.ComponentPlacement;

import emulator.sidplay.SidVicCon;
import emulator.util.file.FileHelper;
import emulator.util.file.FileInfo;
import emulator.util.file.FileInfoSid;
import emulator.util.file.FileLoadStrategy;
import javax.swing.JComboBox;

public class SidConverter {

	private JFrame frmViciousSidConverter;
	private JTextField textSidSource;
	private JTextField textSidTitle;
	private JTextField textSidAuthor;
	private JTextField textSidYear;
	private JTextField textSidSongs;
	private JTextField textSidStart;
	private JTextField textPrgFile;
	private JTextField textLblFile;
	private JTextField textOutputPrg;
	private Preferences cfg;
	private JFileChooser fileChooserSid;
	private JFileChooser fileChooserEmu;
	private JFileChooser fileChooserOut;
	private JComboBox comboSongNo;

	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					SidConverter window = new SidConverter();
					window.frmViciousSidConverter.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the application.
	 */
	public SidConverter() {
	    cfg = Preferences.userNodeForPackage(this.getClass());
		initialize();
		fileChooserSid = new JFileChooser();
		fileChooserEmu = new JFileChooser();
		fileChooserOut = new JFileChooser();
		loadSettings();
	}

	/**
	 * Initialize the contents of the frame.
	 */
	private void initialize() {
	
		frmViciousSidConverter = new JFrame();
		frmViciousSidConverter.setTitle("Vicious SID Converter");
		frmViciousSidConverter.setBounds(100, 100, 440, 482);
		frmViciousSidConverter.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		JLabel lblSource = new JLabel("Source:");
		lblSource.setFont(new Font("Tahoma", Font.BOLD, 13));
		
		JLabel lblSid = new JLabel("SID");
		
		textSidSource = new JTextField();
		textSidSource.setColumns(10);
		
		JButton btnBrowseSid = new JButton("Browse...");
		btnBrowseSid.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				String selected_file = openFileDialog("Load SID", "sid", FileDialog.LOAD, fileChooserSid);
				if (selected_file != null)
				{
					textSidSource.setText(selected_file);
					if (!selected_file.isEmpty())
						updateSid(); 
				}
			}
		});
		
		JLabel lblTitle = new JLabel("Title");
		
		JLabel lblAuthor = new JLabel("Author");
		
		JLabel lblYear = new JLabel("Year");
		
		JLabel lblSongs = new JLabel("Songs");
		
		JLabel lblDefault = new JLabel("Default");
		
		textSidTitle = new JTextField();
		textSidTitle.setEditable(false);
		textSidTitle.setColumns(10);
		
		textSidAuthor = new JTextField();
		textSidAuthor.setEditable(false);
		textSidAuthor.setColumns(10);
		
		textSidYear = new JTextField();
		textSidYear.setEditable(false);
		textSidYear.setColumns(10);
		
		textSidSongs = new JTextField();
		textSidSongs.setEditable(false);
		textSidSongs.setColumns(10);
		
		textSidStart = new JTextField();
		textSidStart.setEditable(false);
		textSidStart.setColumns(10);
		
		JLabel lblSidVicious = new JLabel("SID Vicious:");
		lblSidVicious.setFont(new Font("Tahoma", Font.BOLD, 13));
		
		JLabel lblPrg = new JLabel("PRG");
		
		JLabel lblLbl = new JLabel("LBL");
		
		textPrgFile = new JTextField();
		textPrgFile.setColumns(10);
		
		textLblFile = new JTextField();
		textLblFile.setColumns(10);
		
		JButton btnBrowsePrg = new JButton("Browse...");
		btnBrowsePrg.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				String selected_file = openFileDialog("Load SID Visious", "prg", FileDialog.LOAD, fileChooserEmu);
				if (selected_file != null)
				{
					textPrgFile.setText(selected_file);
					updatePrg();
				}
			}
		});
		
		JButton btnBrowseLbl = new JButton("Browse...");
		btnBrowseLbl.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				String selected_file = openFileDialog("Load Symbols", "lbl", FileDialog.LOAD, fileChooserEmu);
				if (selected_file != null)
				{
					textLblFile.setText(selected_file);
					updateLbl();
				}
			}
		});
		
		JButton btnAnalyze = new JButton("Analyze...");
		btnAnalyze.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				onAnalyze();
			}
		});
		
		JButton btnConvert = new JButton("Convert...");
		btnConvert.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				onConvert();
			}
		});
		
		JLabel lblOutput = new JLabel("Output:");
		lblOutput.setFont(new Font("Tahoma", Font.BOLD, 13));
		
		JLabel lblPrg_1 = new JLabel("PRG");
		
		textOutputPrg = new JTextField();
		textOutputPrg.setColumns(10);
		
		JButton btnBrowseOutputPrg = new JButton("Browse...");
		btnBrowseOutputPrg.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				String selected_file = openFileDialog("Output program", "prg", FileDialog.SAVE, fileChooserOut);
				if (selected_file != null)
				{
					textOutputPrg.setText(selected_file);
					updateOutput();
				}
			}
		});
		
		JLabel lblSong = new JLabel("Song");
		
		comboSongNo = new JComboBox();
		
		GroupLayout groupLayout = new GroupLayout(frmViciousSidConverter.getContentPane());
		groupLayout.setHorizontalGroup(
			groupLayout.createParallelGroup(Alignment.LEADING)
				.addGroup(groupLayout.createSequentialGroup()
					.addGroup(groupLayout.createParallelGroup(Alignment.LEADING)
						.addGroup(groupLayout.createSequentialGroup()
							.addContainerGap()
							.addGroup(groupLayout.createParallelGroup(Alignment.LEADING)
								.addGroup(groupLayout.createSequentialGroup()
									.addGroup(groupLayout.createParallelGroup(Alignment.LEADING)
										.addComponent(lblPrg)
										.addComponent(lblLbl))
									.addPreferredGap(ComponentPlacement.RELATED)
									.addGroup(groupLayout.createParallelGroup(Alignment.LEADING)
										.addComponent(textPrgFile, Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, 259, Short.MAX_VALUE)
										.addComponent(textLblFile, Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, 259, Short.MAX_VALUE)
										.addComponent(btnConvert, Alignment.TRAILING))
									.addPreferredGap(ComponentPlacement.RELATED)
									.addGroup(groupLayout.createParallelGroup(Alignment.TRAILING)
										.addComponent(btnBrowseLbl)
										.addComponent(btnBrowsePrg)))
								.addGroup(groupLayout.createSequentialGroup()
									.addGroup(groupLayout.createParallelGroup(Alignment.LEADING)
										.addComponent(lblSid)
										.addComponent(lblTitle))
									.addGap(18)
									.addGroup(groupLayout.createParallelGroup(Alignment.TRAILING)
										.addGroup(groupLayout.createSequentialGroup()
											.addComponent(textSidSource, GroupLayout.DEFAULT_SIZE, 253, Short.MAX_VALUE)
											.addPreferredGap(ComponentPlacement.RELATED)
											.addComponent(btnBrowseSid)
											.addPreferredGap(ComponentPlacement.RELATED))
										.addComponent(textSidTitle, GroupLayout.DEFAULT_SIZE, 346, Short.MAX_VALUE)))
								.addGroup(groupLayout.createSequentialGroup()
									.addGroup(groupLayout.createParallelGroup(Alignment.LEADING)
										.addComponent(lblYear)
										.addComponent(lblAuthor)
										.addComponent(lblSongs))
									.addGap(18)
									.addGroup(groupLayout.createParallelGroup(Alignment.LEADING)
										.addGroup(groupLayout.createSequentialGroup()
											.addComponent(textSidSongs, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
											.addGap(46)
											.addComponent(lblDefault)
											.addPreferredGap(ComponentPlacement.UNRELATED)
											.addComponent(textSidStart, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
										.addComponent(textSidAuthor, 332, 332, 332)
										.addComponent(textSidYear, 332, 332, 332)))
								.addGroup(groupLayout.createSequentialGroup()
									.addGap(36)
									.addGroup(groupLayout.createParallelGroup(Alignment.LEADING)
										.addComponent(comboSongNo, GroupLayout.PREFERRED_SIZE, 52, GroupLayout.PREFERRED_SIZE)
										.addGroup(groupLayout.createSequentialGroup()
											.addComponent(textOutputPrg, GroupLayout.DEFAULT_SIZE, 259, Short.MAX_VALUE)
											.addPreferredGap(ComponentPlacement.RELATED)
											.addComponent(btnBrowseOutputPrg)))))
							.addGap(115))
						.addGroup(groupLayout.createSequentialGroup()
							.addGap(98)
							.addComponent(btnAnalyze))
						.addGroup(groupLayout.createSequentialGroup()
							.addContainerGap()
							.addComponent(lblOutput))
						.addGroup(groupLayout.createSequentialGroup()
							.addContainerGap()
							.addComponent(lblPrg_1)))
					.addGap(0))
				.addGroup(groupLayout.createSequentialGroup()
					.addContainerGap()
					.addComponent(lblSource)
					.addContainerGap(453, Short.MAX_VALUE))
				.addGroup(groupLayout.createSequentialGroup()
					.addContainerGap()
					.addComponent(lblSidVicious)
					.addContainerGap(427, Short.MAX_VALUE))
				.addGroup(groupLayout.createSequentialGroup()
					.addContainerGap()
					.addComponent(lblSong)
					.addContainerGap(357, Short.MAX_VALUE))
		);
		groupLayout.setVerticalGroup(
			groupLayout.createParallelGroup(Alignment.LEADING)
				.addGroup(groupLayout.createSequentialGroup()
					.addContainerGap()
					.addComponent(lblSource)
					.addPreferredGap(ComponentPlacement.RELATED)
					.addGroup(groupLayout.createParallelGroup(Alignment.BASELINE)
						.addComponent(lblSid)
						.addComponent(textSidSource, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
						.addComponent(btnBrowseSid, GroupLayout.PREFERRED_SIZE, 26, GroupLayout.PREFERRED_SIZE))
					.addPreferredGap(ComponentPlacement.RELATED)
					.addGroup(groupLayout.createParallelGroup(Alignment.BASELINE)
						.addComponent(lblTitle)
						.addComponent(textSidTitle, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
					.addPreferredGap(ComponentPlacement.RELATED)
					.addGroup(groupLayout.createParallelGroup(Alignment.BASELINE)
						.addComponent(lblAuthor)
						.addComponent(textSidAuthor, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
					.addPreferredGap(ComponentPlacement.RELATED)
					.addGroup(groupLayout.createParallelGroup(Alignment.BASELINE)
						.addComponent(lblYear)
						.addComponent(textSidYear, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
					.addPreferredGap(ComponentPlacement.RELATED)
					.addGroup(groupLayout.createParallelGroup(Alignment.BASELINE)
						.addComponent(lblSongs)
						.addComponent(lblDefault)
						.addComponent(textSidSongs, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
						.addComponent(textSidStart, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
					.addGap(18)
					.addComponent(lblSidVicious)
					.addPreferredGap(ComponentPlacement.UNRELATED)
					.addGroup(groupLayout.createParallelGroup(Alignment.BASELINE)
						.addComponent(lblPrg)
						.addComponent(textPrgFile, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
						.addComponent(btnBrowsePrg))
					.addPreferredGap(ComponentPlacement.UNRELATED)
					.addGroup(groupLayout.createParallelGroup(Alignment.BASELINE)
						.addComponent(lblLbl)
						.addComponent(textLblFile, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
						.addComponent(btnBrowseLbl))
					.addGap(18)
					.addComponent(lblOutput)
					.addPreferredGap(ComponentPlacement.UNRELATED)
					.addGroup(groupLayout.createParallelGroup(Alignment.BASELINE)
						.addComponent(lblPrg_1)
						.addComponent(btnBrowseOutputPrg)
						.addComponent(textOutputPrg, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
					.addPreferredGap(ComponentPlacement.UNRELATED)
					.addGroup(groupLayout.createParallelGroup(Alignment.BASELINE)
						.addComponent(lblSong)
						.addComponent(comboSongNo, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
					.addPreferredGap(ComponentPlacement.RELATED, 32, Short.MAX_VALUE)
					.addGroup(groupLayout.createParallelGroup(Alignment.BASELINE)
						.addComponent(btnAnalyze)
						.addComponent(btnConvert))
					.addContainerGap())
		);
		frmViciousSidConverter.getContentPane().setLayout(groupLayout);
	}

	private void loadSettings() {
		fileChooserSid.setCurrentDirectory(new File(cfg.get("SidDir", fileChooserSid.getCurrentDirectory().getAbsolutePath())));
		fileChooserEmu.setCurrentDirectory(new File(cfg.get("EmuDir", fileChooserEmu.getCurrentDirectory().getAbsolutePath())));
		fileChooserOut.setCurrentDirectory(new File(cfg.get("OutDir", fileChooserOut.getCurrentDirectory().getAbsolutePath())));
		String prg_file = ""; // cfg.get("SourcePrg", "");
		String lbl_file = cfg.get("SourceLbl", "");
		if (prg_file.isEmpty())
		{
			File prg = new File("sidplay.prg");
			if (prg.canRead() && prg.isFile())
			{
				prg_file = prg.getAbsolutePath();
				lbl_file = "";
			}
		}
		textPrgFile.setText(prg_file);
		textLblFile.setText(lbl_file);
		if (!prg_file.isEmpty())
		{
			if (lbl_file.isEmpty())
				updatePrg();
			else
				updateLbl();
		}
	}

	private void saveSettings() {
		saveIfNotEmpty("SourcePrg", textPrgFile.getText());
		saveIfNotEmpty("SourceLbl", textLblFile.getText());
		saveIfNotEmpty("SidDir", fileChooserSid.getCurrentDirectory().getAbsolutePath());
		saveIfNotEmpty("EmuDir", fileChooserEmu.getCurrentDirectory().getAbsolutePath());
		saveIfNotEmpty("OutDir", fileChooserOut.getCurrentDirectory().getAbsolutePath());
	}

	private void saveIfNotEmpty(String key, String value) {
		if (value != null && !value.isEmpty()) {
			cfg.put(key, value);
		}
	}
	
	private String openFileDialog(String title, final String ext, int type, JFileChooser fileChooser) {
		fileChooser.setAcceptAllFileFilterUsed(false);
        fileChooser.setFileFilter(new FileFilter() {
            @Override
            public boolean accept(File f) {
                return f.isDirectory() || f.getName().matches(".*\\."+ext);
            }
 
            @Override
            public String getDescription() {
                return "*."+ext;
            }
        });
        if (type == FileDialog.LOAD)
        	fileChooser.showOpenDialog(null);
        else
        	fileChooser.showSaveDialog(null);
        	
        if (fileChooser.getSelectedFile() != null)
        	return fileChooser.getSelectedFile().getAbsolutePath();
        
       return null;
	}

	protected void updateSid() {
		int song_count = 0;
		int default_song = 0;
		String sid_name = textSidSource.getText();
		FileInfo info = FileHelper.getFileInfo(sid_name, FileHelper.guessFileFormat(sid_name));
		if (info == null)
		{
			JOptionPane.showMessageDialog(frmViciousSidConverter, "Unknown file format or file not found.");
		}
		else if (info.getStartAddress() < 0)
		{
			JOptionPane.showMessageDialog(frmViciousSidConverter, "Unknown load address.");
		}
		else
		{
			FileLoadStrategy loader = info.getLoadStrategy();
			if (loader == null)
			{
				JOptionPane.showMessageDialog(frmViciousSidConverter, "Unsupported file format.");
			}
			else
			{
				FileInfoSid sid_file = null;
				try {
					sid_file = (FileInfoSid)info;
				} catch (ClassCastException e) {
				}
				if (sid_file != null)
				{
					textSidTitle.setText(sid_file.getTitle());
					textSidAuthor.setText(sid_file.getAuthor());
					textSidYear.setText(sid_file.getYear());
					textSidSongs.setText(Integer.toString(sid_file.getSongCount()));
					textSidStart.setText(Integer.toString(sid_file.getStartSong()));
					default_song = sid_file.getStartSong();
					song_count = sid_file.getSongCount();
				}
			}
		}
		setSongCount(song_count, default_song);
		saveSettings();
	}
	
	protected void updatePrg() {
		String prg_name = textPrgFile.getText();
		int dot_pos = prg_name.lastIndexOf('.');
		if (dot_pos >= 0)
		{
			String lbl_name = prg_name.substring(0, dot_pos+1) + "lbl";
			File lbl_file = new File(lbl_name);
			if (lbl_file.isFile() && lbl_file.canRead())
			{
				textLblFile.setText(lbl_name);
				updateLbl();
			}
		}
		
	}

	protected void updateLbl() {
		saveSettings();
	}

	protected void updateOutput() {
		saveSettings();
	}

	protected void onAnalyze() {
		final AnalyzerFrame frame = new AnalyzerFrame();
		frame.restorePosition();
        frame.setVisible(true);
        ( new Thread() {
            PrintStream out = frame.createOut();
			PrintStream err = frame.createErr();
        	public void run() {
        		onBeginAnalysis();
				SidVicCon.convertSid(getSongNo(), getSongNo(), textSidSource.getText(), textPrgFile.getText(), textLblFile.getText(), null, out, err);
				onEndAnalysis();
        	}
          }
        ).start();
	}

	protected void onConvert() {
		String output_name = textOutputPrg.getText();
		if (output_name.isEmpty())
		{
			JOptionPane.showMessageDialog(frmViciousSidConverter, "Output file name is empty!");
		}
		else
		{
			final AnalyzerFrame frame = new AnalyzerFrame();
			frame.restorePosition();
	        frame.setVisible(true);
	        ( new Thread() {
	            PrintStream out = frame.createOut();
				PrintStream err = frame.createErr();
	        	public void run() {
	        		onBeginAnalysis();
					SidVicCon.convertSid(getSongNo(), getSongNo(), textSidSource.getText(), textPrgFile.getText(), textLblFile.getText(), textOutputPrg.getText(), out, err);
					onEndAnalysis();
	        	}
	          }
	        ).start();
		}
	}

	private void setSongCount(int song_count, int default_song) {
		comboSongNo.removeAllItems();
		for (int i = 0; i <= song_count; i++)
		{
			comboSongNo.addItem(Integer.toString(i));
		}
		comboSongNo.setSelectedIndex(default_song);
	}

	protected int getSongNo() {
		int song_no = comboSongNo.getSelectedIndex();
		return song_no > 0 ? song_no : 0;
	}

	protected void onBeginAnalysis() {
		frmViciousSidConverter.setEnabled(false);
	}

	protected void onEndAnalysis() {
		frmViciousSidConverter.setEnabled(true);
	}
}
